#include "project.h"

// ----------------------------------------Image---------------------------------------------

void Image::Show_image(const char *image_path, int mode, int n)
{
    string type = string(strrchr(image_path, '.'));

    if (type == ".bmp" || type == ".BMP")
    {
        this->Show_bmp(image_path, mode, n);
    }
    else if (type == ".jpg" || type == ".JPG" || type == "jpeg")
    {
        this->Show_jpg(image_path, mode, n);
    }
    else
    {
        cout << "无法显示该照片格式(当前仅支持bmp、jpg)" << endl;
    }
}

// 通用函数：根据图片尺寸和屏幕尺寸计算显示参数
ImageLayoutResult Image::GetDisplayLayout(
    int image_width, int image_height,    // 图片的宽高
    int target_area_x, int target_area_y, // 目标起始坐标
    int target_area_w, int target_area_h  // 目标区域的宽高
)
{
    ImageLayoutResult result;

    // 1. 判断是否需要缩小
    bool need_scale_down = (image_width > target_area_w || image_height > target_area_h);

    // 2. 计算最终显示尺寸
    int display_width, display_height;
    if (need_scale_down)
    {
        float width_ratio = static_cast<float>(target_area_w) / image_width;
        float height_ratio = static_cast<float>(target_area_h) / image_height;
        float final_ratio = std::min(width_ratio, height_ratio); // 等比例缩放

        display_width = static_cast<int>(image_width * final_ratio);
        display_height = static_cast<int>(image_height * final_ratio);
    }
    else
    {
        display_width = image_width;
        display_height = image_height;
    }

    // 3. 计算图片在安全区域内的居中位置
    int x_in_safe_area = (target_area_w - display_width) / 2;
    int y_in_safe_area = (target_area_h - display_height) / 2;

    // 6. 计算最终在屏幕上的起始坐标
    result.final_x = target_area_x + x_in_safe_area;
    result.final_y = target_area_y + y_in_safe_area;

    // 计算最终映射范围
    result.draw_width = display_width;
    result.draw_height = display_height;

    return result;
}

void Image::Show_bmp(const char *image_path, int mode, int n)
{
    // bmp的图片映射
    FILE *fp = fopen(image_path, "r");
    if (fp == nullptr)
    {
        cout << "失败" << endl;
    }
    struct bitmap_header head;
    struct bitmap_info info;
    fread(&head, sizeof(head), 1, fp);
    fread(&info, sizeof(info), 1, fp);
    if (info.compression)
    {
        struct rgb_quad quad;
        fread(&quad, sizeof(quad), 1, fp);
    }

    // 计算 BMP 每行的填充字节
    int pad = (4 - (info.width * 3) % 4) % 4;
    int bmp_line_size = info.width * 3 + pad;

    // 计算RGB数据的实际大小并分配内存
    long rgb_data_size = (long)bmp_line_size * (long)info.height;
    unsigned char *rgb_data = new unsigned char[rgb_data_size];
    // 移动到像素数据起始位置再读取RGB数据
    fseek(fp, head.offbits, SEEK_SET);     // 假设 offset 是位图数据起始点相对于文件开头的偏移量
    fread(rgb_data, 1, rgb_data_size, fp); // 仅读取实际的RGB数据部分
    fclose(fp);

    int target_area_w = 0, target_area_h = 0, target_area_x = 0, target_area_y = 0;
    if (mode == ARRANGE)
    {
        if (n >= 9)
            return;
        target_area_w = smallPC_x;
        target_area_h = smallPC_y;
        target_area_x = (lcd_wide - this->lcd_wide * 3 / 4) / 2 + (n % 3) * x_Margin;
        target_area_y = (lcd_high - this->lcd_high * 3 / 4) / 2 + (n / 3) * y_Margin;
    }
    else if (mode == NORMAL)
    {
        // 默认目标区域：3/4 屏幕
        target_area_w = lcd_wide * 3 / 4;
        target_area_h = lcd_high * 3 / 4;
        target_area_x = (lcd_wide - target_area_w) / 2;
        target_area_y = (lcd_high - target_area_h) / 2;
    }

    auto layout = this->GetDisplayLayout(info.width, info.height, target_area_x, target_area_y, target_area_w, target_area_h);
    // 获取映射起始地址
    char *fbm = this->framebuffer_ptr + (layout.final_y * lcd_wide + layout.final_x) * 4;
    for (int y = 0; y < layout.draw_height; ++y)
    {
        for (int x = 0; x < layout.draw_width; ++x)
        {
            int src_x = (x * info.width) / layout.draw_width; // 计算现在x,y对应图片映射的位置
            int src_y = (y * info.height) / layout.draw_height;

            // 计算原图中的位置
            int bmp_y = info.height - src_y - 1; // BMP是从下往上存储的
            int src_idx = (bmp_y * bmp_line_size) + src_x * 3;
            int dst_idx = y * lcd_wide + x;

            fbm[dst_idx * 4 + 2] = rgb_data[src_idx + 2]; // B
            fbm[dst_idx * 4 + 1] = rgb_data[src_idx + 1]; // G
            fbm[dst_idx * 4 + 0] = rgb_data[src_idx + 0]; // R
            fbm[dst_idx * 4 + 3] = 0;                     // Alpha通道设置为0
        }
    }

    delete[] rgb_data;
}

void Image::Show_jpg(const char *image_path, int mode, int n)
{
    struct jpeg_info *jinfo = this->jpg2rgb(image_path);

    int target_area_w = 0, target_area_h = 0, target_area_x = 0, target_area_y = 0;
    if (mode == ARRANGE)
    {
        if (n >= 9)
            return;
        target_area_w = smallPC_x;
        target_area_h = smallPC_y;
        target_area_x = (lcd_wide - this->lcd_wide * 3 / 4) / 2 + (n % 3) * x_Margin;
        target_area_y = (lcd_high - this->lcd_high * 3 / 4) / 2 + (n / 3) * y_Margin;
    }
    else if (mode == NORMAL)
    {
        // 默认目标区域：3/4 屏幕，居中
        target_area_w = lcd_wide * 3 / 4;
        target_area_h = lcd_high * 3 / 4;
        target_area_x = (lcd_wide - target_area_w) / 2;
        target_area_y = (lcd_high - target_area_h) / 2;
    }

    auto layout = GetDisplayLayout(
        jinfo->width, jinfo->height,
        target_area_x, target_area_y,
        target_area_w, target_area_h);

    char *fbm = framebuffer_ptr + layout.final_y * lcd_wide * 4 + layout.final_x * 4;

    for (int y = 0; y < layout.draw_height; ++y)
    {
        for (int x = 0; x < layout.draw_width; ++x)
        {
            int src_x = (x * jinfo->width) / layout.draw_width;
            int src_y = (y * jinfo->height) / layout.draw_height;
            int src_idx = (src_y * jinfo->width + src_x) * 3;
            int dst_idx = y * lcd_wide + x;

            fbm[dst_idx * 4 + 2] = jinfo->rgb_data[src_idx + 0]; // R
            fbm[dst_idx * 4 + 1] = jinfo->rgb_data[src_idx + 1]; // G
            fbm[dst_idx * 4 + 0] = jinfo->rgb_data[src_idx + 2]; // B
            fbm[dst_idx * 4 + 3] = 0;
        }
    }

    delete[] jinfo->jpg_data;
    delete[] jinfo->rgb_data;
    delete jinfo;
}

// 把jpg照片的数据存储到结构体中
void Image::load_jpg(const char *jpgname, struct jpeg_info *p)
{
    // 将在jpeg数据到p指向的结构体中
    p->filename = jpgname;
    FILE *fp = fopen(jpgname, "r"); // xxx.jpg

    struct stat st;
    bzero(&st, sizeof(st));
    stat(jpgname, &st);

    p->jpg_size = st.st_size; // JPG图片的大小
    p->jpg_data = new unsigned char[p->jpg_size];

    int n = fread((void *)p->jpg_data, 1, p->jpg_size, fp);
    if (n == 1)
        return;

    if (n < 1)
        perror("加载JPEG图片失败");
    return;

    fclose(fp);
}

// 进行解码(核心)
struct jpeg_info *Image::jpg2rgb(const char *jpgfile)
{
    struct jpeg_info *jinfo = new struct jpeg_info();
    memset(jinfo, 0, sizeof(struct jpeg_info)); // 初始化

    // 0，加载jpeg文件，得到jpg_data
    this->load_jpg(jpgfile, jinfo);

    // 1，声明解码结构体，以及错误管理结构体
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    // 2，使用缺省的出错处理来初始化解码结构体
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);

    // 3，配置该cinfo，使其从 jpgdata 中读取jpgsize个字节
    //    这些数据必须是完整的JPEG数据
    jpeg_mem_src(&cinfo, jinfo->jpg_data, jinfo->jpg_size);

    // 4，读取JPEG文件的头，并判断其格式是否合法
    if (!jpeg_read_header(&cinfo, (boolean) true))
    {
        fprintf(stderr, "jpeg_read_header failed: "
                        "%s\n",
                strerror(errno));
        return NULL;
    }

    // 5，开始解码
    jpeg_start_decompress(&cinfo);

    // 6，显示并保存图片的尺寸信息
    jinfo->width = cinfo.output_width;
    jinfo->height = cinfo.output_height;
    jinfo->bpp = cinfo.output_components;
    // printf("宽：  %d\n", jinfo->width = cinfo.output_width);
    // printf("高：  %d\n", jinfo->height = cinfo.output_height);
    // printf("色深：%d\n", jinfo->bpp = cinfo.output_components);

    // // 计算一行的字节数
    // int row_stride = jinfo.width * jinfo.bpp / 8;

    // 7，根据图片的尺寸大小，分配一块相应的内存rgbdata
    //    用来存放从jpgdata解码出来的图像数据
    unsigned long linesize = cinfo.output_width * cinfo.output_components;
    jinfo->rgb_size = linesize * cinfo.output_height;
    jinfo->rgb_data = new char[jinfo->rgb_size];

    // 8，循环地将图片的每一行读出并解码到rgb_buffer中
    while (cinfo.output_scanline < cinfo.output_height)
    {
        unsigned char *buffer_array[1];
        buffer_array[0] = (unsigned char *)(jinfo->rgb_data + cinfo.output_scanline * linesize);
        jpeg_read_scanlines(&cinfo, buffer_array, 1);
    }

    // 9，解码完了，将jpeg相关的资源释放掉
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);

    return jinfo;
}

void Image::Showfill_image(const char *image_path)
{
    struct jpeg_info *jinfo = this->jpg2rgb(image_path);

    auto layout = GetDisplayLayout(
        jinfo->width, jinfo->height, 0, 0, this->lcd_wide, this->lcd_high);

    char *fbm = framebuffer_ptr + layout.final_y * lcd_wide * 4 + layout.final_x * 4;

    for (int y = 0; y < layout.draw_height; ++y)
    {
        for (int x = 0; x < layout.draw_width; ++x)
        {
            int src_x = (x * jinfo->width) / layout.draw_width;
            int src_y = (y * jinfo->height) / layout.draw_height;
            int src_idx = (src_y * jinfo->width + src_x) * 3;
            int dst_idx = y * lcd_wide + x;

            fbm[dst_idx * 4 + 2] = jinfo->rgb_data[src_idx + 0]; // R
            fbm[dst_idx * 4 + 1] = jinfo->rgb_data[src_idx + 1]; // G
            fbm[dst_idx * 4 + 0] = jinfo->rgb_data[src_idx + 2]; // B
            fbm[dst_idx * 4 + 3] = 0;
        }
    }

    delete[] jinfo->jpg_data;
    delete[] jinfo->rgb_data;
    delete jinfo;
}

// 排列显示
void Image::Show_arrang()
{
    bzero(this->framebuffer_ptr, this->lcd_high * this->lcd_wide * 4);
    for (int index = 0; index < (int)this->imagePathList.size(); index++)
    {
        const char *p = this->imagePathList[index].c_str();
        this->Show_image(p, ARRANGE, index);
    }
}
void Image::onEvent(EventType event, int x, int y)
{
    switch (event)
    {
    case EventType::TOUCH_CLICK_LEFT_DOWN:
    {
        if (!is_arrang && !is_start)
        {
            // 上一张
            if (!this->imagePathList.empty())
            {
                currentIndex = (currentIndex - 1 + imagePathList.size()) % imagePathList.size();
                Show_image(imagePathList[currentIndex].c_str());
            }
        }
        else
        {
            ;
        }

        break;
    }
    case EventType::TOUCH_CLICK_LEFT_UP:
    {
        if (!is_arrang && !is_start)
        {
            bzero(this->framebuffer_ptr, this->lcd_high * this->lcd_wide * 4);
            this->Show_arrang();
            is_arrang = true;
        }
        else if (is_arrang)
        {
            bzero(this->framebuffer_ptr, this->lcd_high * this->lcd_wide * 4);
            this->Show_image(this->imagePathList[currentIndex].c_str());
            is_arrang = false;
        }
        break;
    }
    case EventType::TOUCH_CLICK_RIGHT_DOWN:
    {
        if (!is_arrang && !is_start)
        {
            // 下一张
            if (!this->imagePathList.empty())
            {
                currentIndex = (currentIndex + 1) % imagePathList.size();
                Show_image(imagePathList[currentIndex].c_str());
            }
        }
        else
        {
            ;
        }

        break;
    }
    case EventType::TOUCH_CLICK_RIGHT_UP:
    {
        bzero(this->framebuffer_ptr, this->lcd_high * this->lcd_wide * 4);
        this->Showfill_image(END_PATH);
        should_exit = true;
        break;
    }
    case EventType::TOUCH_SLIDE_LEFT:
    {
        if (!is_arrang && !is_start)
        {
            // 下一张
            if (!this->imagePathList.empty())
            {
                currentIndex = (currentIndex + 1) % imagePathList.size();
                Show_image(imagePathList[currentIndex].c_str());
            }
        }
        else
        {
            ;
        }

        break;
    }
    case EventType::TOUCH_SLIDE_RIGHT:
    {
        if (!is_arrang && !is_start)
        {
            // 上一张
            if (!this->imagePathList.empty())
            {
                currentIndex = (currentIndex - 1 + imagePathList.size()) % imagePathList.size();
                Show_image(imagePathList[currentIndex].c_str());
            }
        }
        else
        {
            ;
        }

        break;
    }
    case EventType::TOUCH_CLICK:
    {
        if (is_start)
        {
            bzero(this->framebuffer_ptr, this->lcd_high * this->lcd_wide * 4);
            this->Show_image(this->imagePathList[this->currentIndex].c_str());
            is_start = false;
        }
        else if (is_arrang)
        {
            int index = get_image_index_by_pos(x, y);
            if (index >= 0 && index < (int)this->imagePathList.size())
            {
                bzero(this->framebuffer_ptr, this->lcd_high * this->lcd_wide * 4);
                this->Show_image(this->imagePathList[index].c_str());
                this->currentIndex = index;
                is_arrang = false;
            }
        }
        else
        {
            ;
        }
        break;
    }

    default:
        break;
    }
}

int Image::get_image_index_by_pos(int x, int y)
{
    bool one_x = (x >= 128 && x <= 380);
    bool two_x = (x >= 386 && x <= 638);
    bool three_x = (x >= 644 && x <= 896);

    bool one_y = (y >= 75 && y <= 221);
    bool two_y = (y >= 227 && y <= 373);
    bool three_y = (y >= 379 && y <= 525);

    if (one_x && one_y)
        return 0;
    else if (one_x && two_y)
        return 3;
    else if (one_x && three_y)
        return 6;
    else if (two_x && one_y)
        return 1;
    else if (two_x && two_y)
        return 4;
    else if (two_x && three_y)
        return 7;
    else if (three_x && one_y)
        return 2;
    else if (three_x && two_y)
        return 5;
    else if (three_x && three_y)
        return 8;

    return -1; // 无效点击
}

// ----------------------------------------Get_touch------------------------------------------------------------------------------------

void Get_touch::Get_Touch_Inf()
{
    struct input_event buf;

    int n = read(this->dev_fd, &buf, sizeof(buf));

    if (n != sizeof(buf))
    {
        return;
    }

    if (buf.type == EV_ABS)
    {
        if (buf.code == ABS_X)
        {
            this->current_x = buf.value;
        }
        else if (buf.code == ABS_Y)
        {
            this->current_y = buf.value;
        }
    }

    if (buf.type == EV_KEY && buf.code == BTN_TOUCH)
    {
        if (buf.value == 1 && this->state == IDLE)
        {
            this->state = TOUCH_DOWN;
            this->has_set_start = false;
            // cout << "进入按压状态" << endl;
        }
        else if (buf.value == 0 && state == TOUCH_DOWN)
        {
            this->end_x = this->current_x;
            this->end_y = this->current_y;

            this->analyzeTouchEvent();

            this->state = IDLE;
        }
    }

    if (this->state == TOUCH_DOWN && buf.type == EV_ABS && !this->has_set_start)
    {
        this->start_x = this->current_x;
        this->start_y = this->current_y;

        this->has_set_start = true;
    }
}

void Get_touch::analyzeTouchEvent()
{
    int dx = this->end_x - this->start_x;

    if (abs(dx) > 200)
    {
        if (dx > 0)
        {
            // cout << "右滑" << endl;
            this->notify(EventType::TOUCH_SLIDE_RIGHT);
        }
        else if (dx < 0)
        {
            // cout << "左滑" << endl;
            this->notify(EventType::TOUCH_SLIDE_LEFT);
        }
        return;
    }

    // 点击区域判断
    bool in_left = (end_x <= 128);
    bool in_right = (end_x >= 896);
    bool in_top = (end_y <= 75);
    bool in_bottom = (end_y >= 525);

    if (in_left && in_bottom)
    {
        // cout << "🔘 点击左下角 → 上一张" << endl;
        this->notify(EventType::TOUCH_CLICK_LEFT_DOWN);
    }
    else if (in_right && in_bottom)
    {
        // cout << "🔘 点击右下角 → 下一张" << endl;
        this->notify(EventType::TOUCH_CLICK_RIGHT_DOWN);
    }
    else if (in_left && in_top)
    {
        // cout << "🔘 点击左上角 → 缩放" << endl;
        this->notify(EventType::TOUCH_CLICK_LEFT_UP);
    }
    else if (in_right && in_top)
    {
        // cout << "🔴 点击右上角 → 退出" << endl;
        this->notify(EventType::TOUCH_CLICK_RIGHT_UP);
    }
    else
    {
        // cout << "👆 其他区域点击" << endl;
        this->notify(EventType::TOUCH_CLICK, this->end_x, this->end_y);
    }
}

// -----------------------------------------------Timeshow-----------------------------------------------------------------------------
void Timeshow::show_time()
{
    if (!this->lcd_mmap_ptr)
        return;

    char buf_time[9];
    time_t t = time(NULL);
    struct tm *p = localtime(&t);
    sprintf(buf_time, "%02d:%02d:%02d", p->tm_hour, p->tm_min, p->tm_sec);

    // 显示时间：从屏幕右上角开始，每个字符宽16像素，间隔2像素
    for(int i = 0; i < 8; i++) {
        // 屏幕宽度1024，每个字符占16像素，右对齐显示
        show_font(1024 - 16*(8 - i) - 2, 0, &buf_time[i]);
    }
}

int Timeshow::show_font(int x, int y, const char *font)
{
    // 确保字体字符有效（只处理ASCII可见字符）
    unsigned char c = static_cast<unsigned char>(*font);
    if (c < 32 || c > 126) {
        return -1; // 非可显示字符
    }

    // 计算字模在文件中的偏移（16x16点阵，每个字符占用32字节）
    unsigned long offset = c * (16 * 16 / 8); // 16x16/8 = 32字节/字符
    unsigned char *p = (unsigned char *)this->A_mmap_ptr + offset;

    // 遍历字模的每一行和每一列
    for (int i = 0; i < 16; i++) { // 16行
        for (int j = 0; j < 2; j++) { // 每行2字节（16位）
            unsigned char byte = *(p + i * 2 + j); // 取当前字节
            for (int k = 0; k < 8; k++) { // 每个字节8位
                // 计算当前点在LCD上的坐标
                int lcd_x = x + j * 8 + k;
                int lcd_y = y + i;

                // 检查坐标是否超出屏幕范围（假设屏幕宽1024，高600）
                if (lcd_x < 0 || lcd_x >= 1024 || lcd_y < 0 || lcd_y >= 600) {
                    continue;
                }

                // 计算帧缓冲区中的像素索引（每个像素4字节）
                int pixel_index = (lcd_y * 1024 + lcd_x) * 4;

                // 根据字模位值设置像素颜色（白色：0xFFFFFFFF，黑色：0x00000000）
                if (byte & (0x80 >> k)) { // 位为1时显示白色
                    *(unsigned int *)(lcd_mmap_ptr + pixel_index) = 0xFFFFFFFF;
                } else { // 位为0时显示黑色
                    *(unsigned int *)(lcd_mmap_ptr + pixel_index) = 0x00000000;
                }
            }
        }
    }

    return 0;
}
void Timeshow::init_ascii()
{
    int A_fd = open("/root/ASCII_16.DZK", O_RDWR);
    if (A_fd == -1)
    {
        cerr << "open" << endl;
    }

    this->A_size = lseek(A_fd, 0, SEEK_END);
    lseek(A_fd, 0, SEEK_SET); // 复位

    void *ptr = mmap(NULL, this->A_size, PROT_READ, MAP_SHARED, A_fd, 0);
    if (ptr == MAP_FAILED)
    {
        cerr << "mmap failed for ascii" << endl;
        close(A_fd);
        return;
    }

    this->A_mmap_ptr = static_cast<char *>(ptr);
}